package ldh.im.fxgui.controller.sub;

import com.jfoenix.controls.JFXDialog;
import javafx.beans.InvalidationListener;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import ldh.im.dto.enums.MessageType;
import ldh.im.fxbase.dao.ImSessionDao;
import ldh.im.fxbase.data.pojo.ImMessage;
import ldh.im.fxbase.data.pojo.ImSession;
import ldh.im.fxbase.util.*;
import ldh.im.fxgui.util.UiUtil;
import ldh.im.fxgui.util.UserHeadImageUtil;
import org.fxmisc.flowless.Cell;
import org.fxmisc.flowless.VirtualFlow;
import org.fxmisc.flowless.VirtualizedScrollPane;
import org.kordamp.ikonli.fontawesome.FontAwesome;
import org.kordamp.ikonli.javafx.FontIcon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URL;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 聊天-》会话-》消息-》消息转发
 */
public class TransferMessageController extends TransferBaseController implements Initializable {

    private static final Logger LOGGER = LoggerFactory.getLogger(TransferMessageController.class);

    @FXML private Pane root;
    @FXML private TextField searchTextField;
    @FXML private StackPane sessionContainer;
    @FXML private ScrollPane selectedSessionScrollPane;
    @FXML private VBox selectedSessionContainer;
    @FXML private TextField leaveMessageTextField;
    @FXML private Button sendMessageBtn;
    @FXML private Label moreLabel;

    private ObservableList<WrapSession> items = FXCollections.observableArrayList();
    private FilteredList<WrapSession> filteredData = new FilteredList<>(items, s -> true);
    private SortedList<WrapSession> sortedList = new SortedList<>(filteredData, this::sessionComparator);
    private VirtualFlow<WrapSession, ?> sessionListView;
    private VirtualizedScrollPane virtualizedScrollPane = null;

    private Set<ImSession> selectImSessions = new HashSet<>();
    private Map<Long, Pane> selectedPaneMap = new HashMap<>();
    private boolean isMoreSelect = false; // 是否多选

    private ImSessionDao imSessionDao = BeanFactory.getInstance().getBean(ImSessionDao.class);


    @Override
    public void initialize(URL location, ResourceBundle resources) {
        sessionListView = VirtualFlow.createVertical(sortedList, m->{
            return Cell.wrapNode(createPane(m));
        });
        virtualizedScrollPane = new VirtualizedScrollPane<>(sessionListView);
        sessionContainer.getChildren().setAll(virtualizedScrollPane);

        loadData();

        searchTextField.textProperty().addListener(l->{
            String searchText = searchTextField.getText().trim();
            filteredData.setPredicate(wrapSession -> wrapSession.getImSession().getName().contains(searchText));
        });

        selectedSessionContainer.getChildren().addListener((InvalidationListener)l->{
            if (selectedSessionContainer.getChildren().size() < 1) {
                sendMessageBtn.setDisable(true);
            } else {
                sendMessageBtn.setDisable(false);
            }

            int size = selectedSessionContainer.getChildren().size();
            size = size > 5 ? 5 : size;
            selectedSessionContainer.setPrefHeight(45 * size);
        });

        sendMessageBtn.setDisable(false);
    }

    @FXML
    private void transferMessage(ActionEvent actionEvent) {
        List<ImMessage> messages = new ArrayList<>();
        messages.addAll(buildTransferMessage());
        messages.addAll(buildLeaveMessage());
        Task task = messageSendService.sendMessage(selectImSessions, messages);
        task.setOnSucceeded(e->{
            if (task.getProgress()>=0.3) { // 本地保存成功
                UiUtil.uiShowMessages(messages);
                if (selectImSessions.size() == 1) {
                    UiUtil.switchMessageSession(selectImSessions.iterator().next());
                }
            }
        });
        task.setOnFailed(e->{
            LOGGER.error("转发消息失败", task.getException());
        });
        close(null);
    }

    @FXML
    private void createSessionAndTransferMessageAct(ActionEvent actionEvent) {
        RegionUtil.FxmlBean<Pane, TransferMessageWithUserController> fxmlBean = RegionUtil.loadFxmlBean(this.getClass().getResource("/fxml/sub/TransferMessageWithUserPane.fxml"));
        JFXDialog childDialog = DialogUtil.show(dialog.getDialogContainer(), fxmlBean.getNode(), false);
        fxmlBean.getController().setDialog(childDialog);
        fxmlBean.getController().setParentDialog(dialog);
        fxmlBean.getController().setTransferImages(transferMessages, isOneToOneTransfer);
    }

    @FXML
    protected void close(ActionEvent actionEvent) {
        if (dialog != null) {
            dialog.close();
        }
    }

    @FXML
    private void moreSelectAct(MouseEvent mouseEvent) {
        isMoreSelect = !isMoreSelect;
        if (!isMoreSelect && (selectedSessionContainer.getChildren().size() > 1 || items.size() > 1)) {
            items.forEach(wrapSession -> wrapSession.setSelectProperty(false));
        }
        if (isMoreSelect) {
            moreLabel.setText("取消多选");
        } else {
            moreLabel.setText("多选");
        }
    }

    private void loadData() {
        String userId = UiUtil.getLoginImUser().getUserId();

        ThreadPoolUtil.submit(()-> {
            try{
               List<ImSession> imSessionList = imSessionDao.getByUserIdAndSize(userId, 1000);
               List<WrapSession> sessions = imSessionList.stream().map(s->new WrapSession(s)).collect(Collectors.toList());
                FxThreadUtil.runLater(()-> {
                    items.addAll(sessions);
                    sessionListView.show(0);
                });
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    private int sessionComparator(WrapSession imSession1, WrapSession imSession2) {
        if (imSession1.getImSession().getUpdateTime() == null) return -1;
        if (imSession2.getImSession().getUpdateTime() == null) return 1;
        return imSession1.getImSession().getUpdateTime().compareTo(imSession2.getImSession().getUpdateTime());
    }

    private Node createPane(WrapSession wrapSession) {
        ImSession imSession = wrapSession.getImSession();

        HBox hBox = new HBox();
        hBox.getStyleClass().add("session-item");
        hBox.setAlignment(Pos.CENTER_LEFT);
        hBox.setSpacing(10);

        RadioButton selectRadio = new RadioButton();
        selectRadio.selectedProperty().bindBidirectional(wrapSession.selectPropertyProperty());

        selectRadio.selectedProperty().addListener((l, o, n)-> selectSession(hBox, imSession, selectRadio));
        hBox.getChildren().add(selectRadio);

        hBox.setOnMouseClicked(e->{
            selectRadio.setSelected(!selectRadio.isSelected());
        });

        if (selectImSessions.contains(imSession)) {
            selectRadio.setSelected(true);
            hBox.getStyleClass().add("selected");
        }

        if (imSession.isP2p()) {
            ImageView imageView = (ImageView) UserHeadImageUtil.createSessionImage(imSession);
            imageView.setFitHeight(40);
            imageView.setFitWidth(40);
            hBox.getChildren().add(imageView);
        } else {
            Pane pane = (Pane) UserHeadImageUtil.createSessionImage(imSession);
            pane.setPrefSize(36, 36);
            pane.setMaxSize(36, 36);
            pane.setMinSize(36, 36);
            hBox.getChildren().add(pane);
        }

        Label tipLabel = new Label(imSession.getName());
        tipLabel.setTooltip(new Tooltip(imSession.getName()));
        hBox.getChildren().add(tipLabel);

        return hBox;
    }

    private void selectSession(HBox hBox, ImSession imSession, RadioButton selectRadio) {
        if (selectRadio.isSelected()) {
            if (selectImSessions.contains(imSession)) return;
            hBox.getStyleClass().add("selected");
        } else if(selectImSessions.contains(imSession)){
            Pane pane = selectedPaneMap.remove(imSession.getId());
            selectedSessionContainer.getChildren().remove(pane);
            selectImSessions.remove(imSession);

            hBox.getStyleClass().remove("selected");
            return;
        }

        if (!isMoreSelect) {
            cleanOtherSelectedSession(imSession);
        }
        Pane pane = createSelectedSessionPane(imSession, selectRadio);
        selectedSessionContainer.getChildren().add(pane);
        selectedSessionScrollPane.setVvalue(1);
        selectImSessions.add(imSession);
        selectedPaneMap.put(imSession.getId(), pane);
    }

    private Pane createSelectedSessionPane(ImSession imSession, RadioButton selectRadio) {
        HBox hBox = new HBox();
        hBox.getStyleClass().add("selected-session-item");
        hBox.setUserData(imSession);

        if (imSession.isP2p()) {
            ImageView imageView = (ImageView) UserHeadImageUtil.createSessionImage(imSession);
            imageView.setFitHeight(40);
            imageView.setFitWidth(40);
            hBox.getChildren().add(imageView);
        } else {
            Pane pane = (Pane) UserHeadImageUtil.createSessionImage(imSession);
            pane.setPrefSize(36, 36);
            pane.setMaxSize(36, 36);
            pane.setMinSize(36, 36);
//            flowPane.setAlignment(Pos.CENTER);
            hBox.getChildren().add(pane);
        }

        Label tipLabel = new Label(imSession.getName());
        tipLabel.setTooltip(new Tooltip(imSession.getName()));

        Region region = new Region();
        HBox.setHgrow(region, Priority.ALWAYS);

        Label closeLabel = new Label();
        closeLabel.getStyleClass().add("close-label");

        FontIcon fontIcon = FontIcon.of(FontAwesome.CLOSE);
        fontIcon.setIconColor(Color.WHITE);

        closeLabel.setGraphic(fontIcon);
        closeLabel.setAlignment(Pos.CENTER);
        closeLabel.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);

        hBox.getChildren().addAll(tipLabel, region, closeLabel);

        closeLabel.setOnMouseEntered(e-> fontIcon.setIconColor(Color.GRAY));
        closeLabel.setOnMouseExited(e-> fontIcon.setIconColor(Color.LIGHTGRAY));
        closeLabel.setOnMouseClicked(e->{
            selectRadio.setSelected(false);
        });
        return hBox;
    }

    private void cleanOtherSelectedSession(ImSession imSession) {
        items.forEach(wrapSession -> {
            if (wrapSession.getImSession() == imSession) return;
            wrapSession.setSelectProperty(false);
        });
    }

    private List<ImMessage> buildLeaveMessage() {
        String text = leaveMessageTextField.getText().trim();
        List<ImMessage> result = new ArrayList<>();
        if (text.equals("")) return result;
        if (selectImSessions.size() < 1) return result;

        for (ImSession imSession : selectImSessions) {
            ImMessage message = messageSendService.buildMessage(imSession, text, MessageType.Text, (ImMessage)null);
            message.setImSession(imSession);
            result.add(message);
        }

        return result;
    }

    private List<ImMessage> buildTransferMessage() {
        List<ImMessage> result = new ArrayList<>();
        for (ImSession imSession : selectImSessions) {
            List<ImMessage> messages = buildTransferMessage(imSession);
            result.addAll(messages);
        }
        return result;
    }

    private class WrapSession {
        private ImSession imSession;
        private BooleanProperty selectProperty = new SimpleBooleanProperty(false);

        public WrapSession(ImSession imSession) {
            this.imSession = imSession;
        }

        public ImSession getImSession() {
            return imSession;
        }

        public boolean isSelectProperty() {
            return selectProperty.get();
        }

        public BooleanProperty selectPropertyProperty() {
            return selectProperty;
        }

        public void setSelectProperty(boolean selectProperty) {
            this.selectProperty.set(selectProperty);
        }
    }

}
